package club.jdiy.dev.zlt.service.impl;

import club.jdiy.admin.util.IdUtil;
import club.jdiy.core.base.JDiyBaseService;
import club.jdiy.core.ex.JDiyFormException;
import club.jdiy.core.sql.ColumnInfo;
import club.jdiy.dev.dao.JDiyUiDao;
import club.jdiy.dev.entity.JDiyUi;
import club.jdiy.dev.meta.*;
import club.jdiy.dev.types.*;
import club.jdiy.dev.zlt.service.JDevViUiService;
import club.jdiy.utils.JsonUtils;
import club.jdiy.utils.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.regex.Pattern;


@SuppressWarnings("unused")
@Service
public class JDevViUiServiceImpl extends JDiyBaseService<JDiyUi, JDiyUiDao, String>
        implements JDevViUiService {

    @Override
    @Transactional
    public JDiyUi logicSave(JDiyUi vo) {
        JDiyUi po = dao.findById(vo.getId()).orElseGet(() -> {
            JDiyUi po1 = new JDiyUi();
            po1.setType(vo.getType());
            po1.setId(IdUtil.newId());
            po1.setBindType(vo.getBindType());
            if ("table".equals(vo.getBindType())) {
                po1.setMainTable(vo.getMainTable());
            } else {
                po1.setMainEntity(vo.getMainEntity());
                po1.setMainTable(context.getDao().getEntityMeta(vo.getMainEntity()).getTableName());
            }

            return po1;
        });
        po.setName(vo.getName());

        //tds:
        ViewUiMeta vum = new ViewUiMeta();
        Collection<ColumnInfo> columnInfos = context.getDao().getTableInfo(po.getMainTable()).getColumns().values();
        int i = 0;
        ViewTdMeta[] cs = new ViewTdMeta[columnInfos.size()];
        for (ColumnInfo fv : columnInfos) {
            ViewTdMeta c1 = new ViewTdMeta();
            c1.setId(IdUtil.newId());
            c1.setField(fv.getName());
            String label = fv.getName();
            if(StringUtils.hasText(fv.getComment())){
                label = fv.getComment();
                if(label.length()>8)label=label.substring(0,8);
            }
            c1.setLabel(fv.isPrimaryKey() ? "ID" : label);//todo XXXXXXXXX
            c1.setType(OutTpl.direct);
            c1.setRow(i / 2);
            c1.setCol(i % 2);
            cs[i++] = c1;
        }
        vum.setTds(cs);

        //buttons:
        JDiyUi inPage = dao.findFirstByTypeAndMainTable(UiTpl.form, po.getMainTable());
        ButtonMeta b2 = new ButtonMeta();
        b2.setId(IdUtil.newId());
        b2.setText("修改");
        b2.setIcon("layui-icon-edit");
        b2.setWidth(640);
        b2.setHeight(480);
        b2.setAct(BtnActTpl.edit);
        b2.setTarget(BtnTargetTpl.dialog);
        if (inPage != null) {
            b2.setGoPageId(inPage.getId());
            b2.setGoPageType(inPage.getType());
        }

        ButtonMeta b3 = new ButtonMeta();
        b3.setId(IdUtil.newId());
        b3.setText("删除");
        b3.setIcon("layui-icon-delete");
        b3.setAct(BtnActTpl.del);
        b3.setTarget(BtnTargetTpl.ajaxTodo);
        b3.setBg("#ff5722");
        b3.setConfirm("您真的要删除此条记录吗？");

        ButtonMeta b4 = new ButtonMeta();
        b4.setId(IdUtil.newId());
        b4.setAct(BtnActTpl.page);
        b4.setText("如果不需要管理按钮，可全部删除");
        b4.setIcon("layui-icon-tips");
        b4.setBg("#c71585");

        vum.setBtn(true);
        vum.setBtns(new ButtonMeta[]{b2, b3,b4});

        vum.setTabPos(1);
        ViewTabMeta tab1 = new ViewTabMeta();
        tab1.setId(IdUtil.newId());
        tab1.setType(1);
        tab1.setLabel("子表tab1");

        ViewTabMeta tab2 = new ViewTabMeta();
        tab2.setId(IdUtil.newId());
        tab2.setType(2);
        tab2.setLabel("子表tab2");
        tab2.setGoUrl("${ctx}/mgmt/foo/bar?id=${vo.id}");

        ViewTabMeta tab3 = new ViewTabMeta();
        tab3.setId(IdUtil.newId());
        tab3.setType(1);
        tab3.setLabel("如果不需要子表tab，可全部删除");
        vum.setTabs(new ViewTabMeta[]{tab1, tab2,tab3});


        po.setMetaData(JsonUtils.stringify(vum));
        return dao.save(po);
    }

    private static final Pattern ont_onv = Pattern.compile("^[^|]+\\|[^|]+$");
    private static final Pattern h_item = Pattern.compile("^([^:\n]+:[^:\n]+)(\n([^:\n]+:[^:\n]+))*$");

    @Override
    @Transactional
    public void saveDesign(String uid, ViewUiMeta uiMeta) {
        JDiyUi pg = dao.findById(uid).orElseThrow(
                () -> new JDiyFormException("表单界面信息不存在或被删除(uiId=" + uid + ")", "")
        );
        if (StringUtils.isEmpty(uiMeta.getPageHandler())) uiMeta.setPageHandler(null);

        Map<String, String> repeatSet = new HashMap<>();
        String objId;
        for (ViewTdMeta td : uiMeta.getTds()) {
            objId = td.getId();
            if (td.getJoinType() != null) {
                switch (td.getJoinType()) {
                    case manyToOne:
                        if (StringUtils.isEmpty(td.getJoinTable()))
                            throw new JDiyFormException("选中内容输出为“多对一”外联字段查，但您还没有选择外联表！", "jdiyviobj_" + objId);
                        if (StringUtils.isEmpty(td.getJoinField()))
                            throw new JDiyFormException("选中内容输出为“多对一”外联字段查，但您还未选择外联表的显示字段！", "jdiyviobj_" + objId);
                        break;
                    case oneToMany:
                        if (StringUtils.isEmpty(td.getJoinTable()))
                            throw new JDiyFormException("选中内容输出为一对多显示，但您还没有选择外联表。", "jdiyviobj_" + objId);
                        if (StringUtils.isEmpty(td.getJoinRef()))
                            throw new JDiyFormException("选中内容输出为一对多显示，但您还没有选择外键字段。", "jdiyviobj_" + objId);
                        if (StringUtils.isEmpty(td.getJoinField()))
                            throw new JDiyFormException("选中内容输出为一对多显示，但您还未选择外联表的显示字段。", "jdiyviobj_" + objId);
                        break;
                    case manyToMany:
                        if (StringUtils.isEmpty(td.getJoinTable()))
                            throw new JDiyFormException("选中的内容输出为多对多关联，但您还没有选择外联表。", "jdiyviobj_" + objId);
                        if (StringUtils.isEmpty(td.getJoinField()))
                            throw new JDiyFormException("选中的内容输出为多对多关联，但您还未选择外联表的显示字段。", "jdiyviobj_" + objId);
                        if (StringUtils.isEmpty(td.getManyTable()))
                            throw new JDiyFormException("选中的内容输出为多对多关联，但您还没有选择中间表。", "jdiyviobj_" + objId);
                        if (StringUtils.isEmpty(td.getManyField0()))
                            throw new JDiyFormException("选中的内容输出为多对多关联，但您还没有选择中间表的正向关联字段。", "jdiyviobj_" + objId);
                        if (StringUtils.isEmpty(td.getManyField1()))
                            throw new JDiyFormException("选中的内容输出为多对多关联，但您还没有选择中间表的反向关联字段。", "jdiyviobj_" + objId);
                        if (td.getManyField1().equals(td.getManyField0()))
                            throw new JDiyFormException("选中的内容输出为多对多关联，正向和反向关联字段不能是同一个。", "jdiyviobj_" + objId);
                        break;
                }
            } else td.setJoinType(JoinTypeTpl.none);

            if (td.getType() == null) td.setType(OutTpl.dict);
            else
                switch (td.getType()) {
                    case kv:
                        if (StringUtils.isEmpty(td.getKv()))
                            throw new JDiyFormException("选中的内容为“键值转键名”的显示方式，但[键值:键名]列表没有填写。", "jdiyviobj_" + objId);
                        for (String s : td.getKv().trim().split("\n")) {
                            if (StringUtils.isEmpty(s)) continue;
                            int n = s.split(":").length;
                            if (n < 2 || n > 3)
                                throw new JDiyFormException("选中的内容为[键值:键名]列表输入格式不正确。详见配置框右侧帮助。", "jdiyviobj_" + objId);
                        }
                        break;
                    case dict:
                        if (StringUtils.isEmpty(td.getDictId()))
                            throw new JDiyFormException("选中的内容为“数据字典”的显示方式，但您还没有选择字典．", "jdiyviobj_" + objId);
                        break;
                    case tree:
                        if (StringUtils.isEmpty(td.getTreeId()))
                            throw new JDiyFormException("选中的内容为“树层级”的显示方式，但您还没有选择树形。", "jdiyviobj_" + objId);
                        break;
                    case tpl:
                        if (StringUtils.isEmpty(td.getTpl()))
                            throw new JDiyFormException("选中的内容为“自定义模板”显示，但您还没有输入模板代码内容。", "jdiyviobj_" + objId);
                        break;
                }
        }

        if (uiMeta.isBtn() && uiMeta.getBtns() != null) {
            __saveBtns(uiMeta.getBtns());
        }
        if (uiMeta.getTabPos() != 0 && uiMeta.getTabs() != null) {
            __saveTabs(uiMeta.getTabs());
        }

        pg.setMetaData(JsonUtils.stringify(uiMeta));
        dao.save(pg);
    }

    private void __saveBtns(ButtonMeta[] buttons) {
        for (ButtonMeta vo : buttons) {
            if (StringUtils.isEmpty(vo.getText()) && StringUtils.isEmpty(vo.getIcon())) {
                throw new JDiyFormException("选中的按钮“按钮文字”或者“显示图标”至少需要设置一个。", "jdiyviobj_" + vo.getId());
            }
            if (BtnActTpl.edit == vo.getAct() || BtnActTpl.page == vo.getAct() || BtnActTpl.link == vo.getAct()) {
                if (BtnActTpl.link != vo.getAct()) {
                    if (StringUtils.isEmpty(vo.getGoPageId()))
                        throw new JDiyFormException("选中的按钮未设置要打开的目标界面！", "jdiyviobj_" + vo.getId());
                    else {
                        try {
                            UiMeta uim = dao.getUiMeta(vo.getGoPageId());
                            vo.setGoPageType(uim.getType());
                        } catch (Exception ex) {
                            ex.printStackTrace();
                            throw new JDiyFormException("获取目标界面类型失败！", "jdiyviobj_" + vo.getId());
                        }
                    }
                }
                if (BtnActTpl.link == vo.getAct() && StringUtils.isEmpty(vo.getOutLink()))
                    throw new JDiyFormException("目标按钮未设定外链地址。", "jdiyviobj_" + vo.getId());
                if (BtnTargetTpl.dialog == vo.getTarget()) {
                    if (vo.getWidth() == null || vo.getWidth() < 300)
                        throw new JDiyFormException("目标按钮弹框宽度设置不正确(建议设置为300~960之间)。", "jdiyviobj_" + vo.getId());
                    if (vo.getHeight() == null || vo.getHeight() < 150)
                        throw new JDiyFormException("目标按钮弹框高度设置不正确(建议设置为180~720之间)。", "jdiyviobj_" + vo.getId());
                }

            } else {
                vo.setTarget(BtnTargetTpl.ajaxTodo);
                if (BtnActTpl.update == vo.getAct()) {
                    if (StringUtils.isEmpty(vo.getUpdateCode()))
                        throw new JDiyFormException("目标按钮操作类型为“更新字段值”，但未填写要更新的字段内容。请在右侧属性栏中修改。", "jdiyviobj_" + vo.getId());
                    if (!update_code_p.matcher(vo.getUpdateCode()).matches())
                        throw new JDiyFormException("目标按钮更新的字段内容格式输入不正确，需要为：“字段名=值”的格式，一行填一个，中间不要出现多个空行或其它无关字符。", "jdiyviobj_" + vo.getId());
                    String ss = vo.getUpdateCode().trim().replaceAll("\\s*,*\\s*([\\n\\r])+\\s*,*\\s*", "\n");
                    vo.setUpdateCode(ss);
                } else if (BtnActTpl.ajax == vo.getAct()) {
                    if (StringUtils.isEmpty(vo.getAjaxUrl()))
                        throw new JDiyFormException("选中的按钮未配置要请求的自定义ajax页面地址。", "jdiyviobj_" + vo.getId());
                }
            }
            if (StringUtils.hasText(vo.getGrantCode()) && !codeP.matcher(vo.getGrantCode()).matches()) {
                throw new JDiyFormException("按钮权限代码只能为英文字母(数字或下划线组合），且不要以数字开头(符合变量命名规范)", "jdiyviobj_" + vo.getId());
            }
            if (StringUtils.hasText(vo.getConditionShow())) {
                if (vo.getConditionShow().contains("${") || vo.getConditionShow().contains("<>"))
                    throw new JDiyFormException("按钮显示条件配置可能有误，请检查。", "jdiyviobj_" + vo.getId());
            }
        }
    }

    private void __saveTabs(ViewTabMeta[] tabs) {
        for (ViewTabMeta vo : tabs) {
            if (StringUtils.isEmpty(vo.getLabel())) {
                throw new JDiyFormException("选中的Tab标题未填写。", "jdiyviobj_" + vo.getId());
            }
            if (vo.getType()==null||vo.getType() != 2) vo.setType(1);
            switch (vo.getType()) {
                case 1:
                    if (StringUtils.isEmpty(vo.getGoPageId()))
                        throw new JDiyFormException("选中的Tab未配置目标界面,请在右侧属性面板中指定。", "jdiyviobj_" + vo.getId());
                    try {
                        UiMeta uim = dao.getUiMeta(vo.getGoPageId());
                        vo.setGoPageType(uim.getType());
                    } catch (Exception ex) {
                        ex.printStackTrace();
                        throw new JDiyFormException("获取目标界面类型失败！", "jdiyviobj_" + vo.getId());
                    }
                    break;
                case 2:
                    if (StringUtils.isEmpty(vo.getGoUrl()))
                        throw new JDiyFormException("选中的Tab为自定义页面，但您还未配置页面地址,请在右侧属性面板中指定。", "jdiyviobj_" + vo.getId());
                    break;
            }
        }
    }

    static Pattern codeP = Pattern.compile("^[a-zA-Z][a-zA-Z0-9_]+$");
    private static final Pattern update_code_p = Pattern.compile("^(\\s*`*[a-zA-Z0-9_]+?`*\\s*=\\s*'*[^'=]+?'*\\s*)(\n(\\s*`*[a-zA-Z0-9_]+?`*\\s*=\\s*'*[^'=]+?'*\\s*))*$");

}
